Software C & C++ Coding Standards ~ Scope ~ Software source code shall conform to these standards. These standards are derived from common coding practices used in commercial software development. These standards shall be used critique code in the formal review process. ~ Coding Standards ~ 1.0 Specification/Implementation: 1.1 Software shall be designed and implemented in a modular architecture. A software module is defined as a C file (*.c) or a C++ class (*.cc or *.cpp). A module shall have an explicit public interface. A module shall have a specification which defines the data and behavior of the module. The specification for a module shall be in the form of a *.h file, for a *.c file and *.hh file, for a *.cc or *.cpp file. 1.2 The specification file shall include all *.h and *.hh files required by the source implementation. The implementation file shall include only one *.h or *.hh which is the specification for the implementation. Figure 1.0 Specification/Implementation Example Specification C C++ /* motor.h */ // motor.hh #ifndef MOTOR_H #ifndef MOTOR_HH #define MOTOR_H #define MOTOR_HH #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include "parts.h" #include "parts.hh" #define MAXMPH 85 const int MAXMPH = 85; extern int fuel; int start ( void ); class Motor int accel ( int rate ); { int stop ( void ); private: int fuel; #ifdef __cplusplus public: } Motor() { fuel = 0; } #endif int start ( void ); int accel ( void ); #endif int stop ( void ); /* eof motor.h */ ~Motor(){ }; // eof motor.hh Implementation C C++ /* motor.c */ // motor.cc #include "motor.h" #include "motor.hh" int int start ( void ) Motor::start ( void ) { { fuel = 10; fuel = 10; } } int int accel ( int rate ) Motor::accel ( int rate ) { { fuel = rate; fuel = rate; } } int int stop ( void ) Motor::stop ( void ) { { fuel = 0; fuel = 0; } } /* eof motor.c */ // eof motor.cc 2.0 Independent Compilation 2.1 A module shall be completely defined by it's specification. Client (consumer) modules which need to use a server (producer) module's implementation shall include the specification for the server module. A module shall be capable of being compiled without dependencies on other modules. The output of the compiling phase shall be object files for each compiled module. C Compilation C++ Compilation gcc -c motor.c gcc -c motor.cc 3.0 Module Coupling and Global Data 3.1 Modules shall be as loosely coupled as possible. This requirement mandates the minimization of global data and to maximize the encapsulation data within a module. All class data shall be private with public accessor or member functions. For global data global data shall be encapsulated within a structure. 3.2 Functions shall receive all external data through the function (public) interface or through the encapsulation of the class. The use of global data shall be explicitly denoted by the use of the structure binding. 3.3 Binding the global data within a structure, reduces the possibility of name space collisions with other global data. The name of the should explicitly detail the data as global. Global data shall be declared and initialized in the main module. All other use of the global data shall be through the use of external references. Global references shall be defined in a file indicated as global. Example of global data: /* main.c */ #include "global.h" #include "motor.h" /* create global data */ global_MotorType Gbl_Motor; int main ( void ) { /* using global data &/ Gbl_Motor.fuel = 23; strcpy(Gbl_Motor.data,"04301997"); ... } /* eof main.c */ /* global.h */ #ifndef GLOBAL_H #define GLOBAL_H #ifdef __cplusplus extern "C" { #endif typedef struct { int fuel; char date[9]; } global_MotorType; extern global_MotorType Gbl_Motor; #ifdef __cplusplus } #endif #endif /* eof global.h */ 3.4 The compiler will identify undefined symbols or duplicate symbols at compile time. Undefined symbols are symbols which have not been included into the compiled source. Using the appropriate include files or libraries are required to resolve undefined symbols. Duplicate symbols are usually the result of not using #ifdef ... #endif wrappers around header files. 4.0 Scoping Rules 4.1 Scope rules define the visibility of an object. An object can have global, file, function and local (block) scope. Global scope was previously covered in the previous section. 4.2 File scope is defined by using the key word static. Objects declared static are only visible in the file for which they are defined. File Scope Example: #define MAXCOLORS 16 static char * PixelColor[MAXCOLORS] = { "BLACK","WHITE","BLUE","GREEN","RED","ORANGE", "VIOLET","CYAN","GREY","PURPLE","PINK","BROWN", "LTGREEN","LTBLUE","NONE" }; 4.3 Function scope is defined within the opening and closing braces of a function. C syntax requires that data be declared at the beginning of the scope before and executable statements. C++ allows the programmer the flexibility to declare an object anywhere in the scope. Special care must be taken to ensure that an objects initialization and use is after it's declaration within the function scope. 4.4 Local or block scope is associated with the open and close braces. These braces are also declare scope for "if", "else", "while", "for", "switch" and "case" statements. Local scope shall be used to create code which is easily read and maintained. 4.5 Braces shall be used on a single line to denote a change in scope. All code and comments bound to a scope shall be indented 2 spaces. Reference the following example as the proper pro-grammatic scoping. int Read_Line ( char * line ) { int index = 0; for ( index = 0; index < strlen(line); index++ ) { printf ( "%c", line[index] ); } printf ( "\n" ); } 4.6 All "if" and "else" statements shall be delimited by scope. Stand-alone "if" statements are prone to logic errors during maintenance. Code can inadvertently fall outside of a conditional block. The following example illustrates how inserting a debug statement in the wrong place during maintenance can cause the program to ALWAYS exit regardless of the value of "x". if ( x < 10 ) printf ( "DEBUG: Exiting on x = %d\n", x ); exit(1); printf ( "DEBUG: Continuing...\n" ); The correct method for scope the previous "if" statement is shown below. if ( x < 10 ) { printf ( "DEBUG: Exiting on x = %d\n", x ); exit(1); } printf ( "DEBUG: Continuing...\n" ); Local scope for "switch", "case" and "default" can make code easier to read and to me maintained. switch ( c ) { case 'A': { Execute_A(); } break; case 'B': { Execute_B(); } break; default: { printf ( "Error: %c unsupported\n", c ); } } 4.7 Local scope can be used anywhere to denote a block of execution. All code within this block must be indented 2 spaces. Local scope maybe used to declare variable anywhere with a function. void Show ( void ) { int i = 0; int x = 10; for ( i=0; i 0 ) { realloc ( mem_pointer, size ); } 6.7 The lines of code, LOC, for a function should be kept to a nominal level of 200 lines. Function exceeding this LOC are difficult to navigate and maintain. Large functions will require more testing than simple small functions. The ideal function is small and of single purpose. 6.8 Source code lines shall be less than or equal to 80 characters in width. This measurement is compliant with the standard terminal width. Code written to the standard terminal width will display and/or print correctly. 6.9 The use of the pre/post increment/decrement operators shall be limited to single purpose statements. The use of pre-increment and pre-decrement operators is problematic when used in compound statements. It is difficult to predict the precedence of operations when operators are compounded. /* confusing code */ *++p = *--p + 1; Nextptr = *p+++1; 6.10 The use of in-line functions shall be used over macro calls. Macros are substituted at compile time and have no context in a symbolic debugger. Macros are extremely difficult to debug and are difficult to code correctly. 6.11 The use of constants created with the ANSI key word 'const' shall be used instead of #define constants. Constants declared with '#define' do not comply with the C or C++ language syntax and have no context within the symbolic debugger. The compiler cannot check the type of '#define' constants. 6.12 The use of the '?' operator shall be restricted to macros. The '?' operator implies and 'if - else' construct. It is better to use an 'if - else' construct in normal code implementation. 6.13 Configuration control or copyright marks are usually required within each code file. Each file shall contain the required configuration and/or copyright statements. 6.14 Each function and file shall contain at least 10 percent white space. White space creates code which is easy to read and understand. 6.15 Each function and file shall contain at least 10 percent comments. Comments are essential for the maintenance of source code modules. 6.16 Function names shall be descriptive to the nature of the function. The use of mixed case and underscores shall be used to create multi-word function names. void ShowTheVertex ( void ); int Set_X ( void ); 6.17 User defined type names shall be in proper case. This includes all typedefs, classes and structures. class Vertex {}; typedef struct { int x; int y; int z; } CoordinateType; 6.18 Symbolic constants shall be used for all numeric constants. These constants shall be defined in the appropriate header files. Symbolic constants shall be named to convey the intent of use. Symbolic constants shall be named in upper case. /* specification file */ const double PI = 3.14159; const int NAMESIZE = 11; /* implementation file */ char name[NAMESIZE];